﻿using System;
//using System.Diagnostics;
using System.Net;
using System.Net.Sockets;

namespace MonoMobile.Dialogs.Remote.Discovery
{
    public class UsidClient : IDisposable
    {
        private UdpClient _udpClient;
		private IPEndPoint _receiveEndPoint;
        bool _stopped;
		
        public int LocalPort  { get; set; }
		public bool EnableBroadcast  { get; set; }
        public event DataReceivedHandler DataReceived;

        public static IPAddress BroadcastAddress
        {
            get { return new IPAddress(0xffffffffL); }
        }

        public UsidClient()
        {
        }

        ~UsidClient()
        {
            Dispose();
        }

        private class UdpState
        {
            public IPEndPoint EndPoint { get; set; }
            public UdpClient UdpClient { get; set; }
        }

        public bool Start()
        {
            if (_udpClient != null)
                return false;
            _stopped = false;

            _receiveEndPoint = new IPEndPoint(IPAddress.Any, LocalPort);
            _udpClient = new UdpClient(_receiveEndPoint);
			_udpClient.EnableBroadcast = EnableBroadcast;

            var s = new UdpState { EndPoint = _receiveEndPoint, UdpClient = _udpClient };
            _udpClient.BeginReceive(new AsyncCallback(requestCallback), s);
			
            return true;
        }

        public bool Stop()
        {
            if (_udpClient == null)
                return false;
            _stopped = true;

            _udpClient.Close();
            _udpClient = null;

            return true;
        }

        private void requestCallback(IAsyncResult ar)
        {
            if (_stopped) return;
            var udpState = (UdpState)(ar.AsyncState);
            var udpClient = udpState.UdpClient;
            var endPoint = udpState.EndPoint;

            try
            {
                Byte[] receiveBuffer = udpClient.EndReceive(ar, ref endPoint);

	            var s = new UdpState { EndPoint = _receiveEndPoint, UdpClient = _udpClient };
    	        _udpClient.BeginReceive(new AsyncCallback(requestCallback), s);

				if (DataReceived != null)
                    DataReceived(this, new DataReceivedEventArgs(receiveBuffer, endPoint));
            }
            catch (SocketException e)
            {
                if (e.ErrorCode != 995)
                    throw;
            }
        }

        public bool SendTo(string address, int port, byte[] data)
        {
            return SendTo(System.Net.IPAddress.Parse(address), port, data);
        }

        public bool SendTo(IPAddress address, int port, byte[] data)
        {
            return SendTo(new IPEndPoint(address, port), data);
        }

        public bool SendTo(IPEndPoint endPoint, byte[] data)
        {
            if (_udpClient == null)
                return false;

            try
            {
                _udpClient.Send(data, data.Length, endPoint);

            }
            catch (Exception)
            {
//                Debug.Print(e.ToString());
                return false;
            } 
            return true;
        }

        #region IDisposable Members

        public void Dispose()
        {
            Stop();
        }

        #endregion
    }
}
